package com.project.aaos.job;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.TypeReference;
import com.project.aaos.config.Config;
import com.project.aaos.dao.ApproAbnormalInfoMapper;
import com.project.aaos.dao.ApproFileInfoMapper;
import com.project.aaos.dao.ApproLineInfoMapper;
import com.project.aaos.exception.AAOSException;
import com.project.aaos.pojo.ApproAbnormalInfo;
import com.project.aaos.pojo.ApproFileInfo;
import com.project.aaos.pojo.ApproLineInfo;
import com.project.aaos.util.FixedLenUtil;
import org.apache.commons.io.FileUtils;
import org.apache.ibatis.session.SqlSession;
import org.quartz.Job;
import org.quartz.JobExecutionContext;
import org.quartz.JobExecutionException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.*;
import java.sql.Timestamp;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;

public class InDbJob implements Job {

    private Logger log = LoggerFactory.getLogger(InDbJob.class);

    private ApproFileInfoMapper approFileMapper;
    private ApproAbnormalInfoMapper approAbnormalMapper;
    private ApproLineInfoMapper approLineMapper;

    @Override
    public void execute(JobExecutionContext jobExecutionContext) throws JobExecutionException {

        SqlSession session = (SqlSession) jobExecutionContext.getJobDetail().getJobDataMap().get("session");
        approFileMapper = session.getMapper(ApproFileInfoMapper.class);
        approAbnormalMapper = session.getMapper(ApproAbnormalInfoMapper.class);
        approLineMapper = session.getMapper(ApproLineInfoMapper.class);

        Config config = (Config) jobExecutionContext.getJobDetail().getJobDataMap().get("config");
        perform(config);

    }

    private void perform(Config config){

        String bank = config.getBank();
        List<String> fileNameList = queryFileName(config);
        //文件夹中没有满足要求的文件,本周期任务结束
        if (fileNameList.size() == 0){
            log.info("[ AAOS ] {} (DBModule) no approve file detected,task over",bank);
            return;
        }
        for (String fileName:fileNameList){

            //根据file config生成ApproFileInfo对象
            ApproFileInfo approFileInfo = createApproFile(fileName,config);

            //如果关闭空文件处理，则空文件备份后直接删除，不走后续流程
            if (isEmptyFile(approFileInfo)&&!"true".equals(config.getEmptyFileHandle())){
                handleEmptyFile(approFileInfo,config);
                log.info("[ AAOS ] {} (DBModule) {} is a empty file, backup and pass it",bank,fileName);
                continue;
            }

            log.info("[ AAOS ] {} (DBModule) {} start insert ",bank,fileName);
            //1.File入库
            ApproFileInfo approFileInfoInDb = approFileMapper.selectByFileName(approFileInfo.getFileName());
            if (approFileInfoInDb!=null){
                handleDuplicateFile(approFileInfo,config);
                log.error("[ AAOS ] {} (DBModule) FileNameExistError: {} has exist,move to error directory",bank,fileName);
                continue;
            }

            approFileMapper.insert(approFileInfo);
            log.info("[ AAOS ] {} (DBModule) {} file insert over",bank,fileName);

            //2.Line入库
            int count = lineInDb(approFileInfo,config," ");
            if (count == -1){
                continue;
            }
            log.info("[ AAOS ] {} (DBModule) {} line insert over",bank,fileName);
            //3.备份
            backUp(approFileInfo,config," ");
            //4.删除源文件及.ok文件
            deleteSour(approFileInfo,config," ");
            //5.更改状态
            approFileInfo.setState(1);
            approFileInfo.setRecordNum(count);
            approFileMapper.updateByPrimaryKeySelective(approFileInfo);
            log.info("[ AAOS ] {} (DBModule) {} change file state to 1,insert over",bank,fileName);
        }
        log.info("[ AAOS ] {} (DBModule) task over",bank);

    }

    private void handleEmptyFile(ApproFileInfo approFileInfo, Config config) {

        File sourFile = new File(approFileInfo.getFilePath());
        String fileName = approFileInfo.getFileName();
        //1.删除源文件及.ok文件
        sourFile.delete();
        new File(config.getSourDir()+"/"+fileName.substring(0,fileName.lastIndexOf("."))+".ok").delete();
        //2.备份
        try {
            new File(config.getBackDir()+"/"+fileName).createNewFile();
        } catch (IOException e) {
            log.error(e.getMessage());
            throw new AAOSException(e.getMessage());
        }
    }

    private void handleDuplicateFile(ApproFileInfo approFileInfo,Config config){
        ApproAbnormalInfo approAbnormalInfo = new ApproAbnormalInfo(approFileInfo);
        approAbnormalMapper.insert(approAbnormalInfo);
        //备份入错误文件夹
        backUpToErr(approFileInfo,config);
        //删除源文件及ok文件
        deleteSour(approFileInfo,config," ");
    }

    private boolean isEmptyFile(ApproFileInfo approFileInfo) {

        File file = new File(approFileInfo.getFilePath());
        if (file.length()==0){
            return true;
        }
        return false;
    }

    private ApproFileInfo createApproFile(String fileName, Config config) {

        ApproFileInfo approFileInfo = new ApproFileInfo();
        approFileInfo.setFileName(fileName);
        approFileInfo.setBank(config.getBank());
        approFileInfo.setFilePath(config.getSourDir()+"/"+fileName);

        //处理文件名中的日期 截取String中的日期
        String fileTime = fileName.substring(0,fileName.lastIndexOf("-"));
        fileTime = fileTime.substring((fileTime.lastIndexOf("-")+1));
        //将时间拼接成YYYYMMDDhhmiss格式
        SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyymmdd");
        Date date = null;
        try {
            date = simpleDateFormat.parse(fileTime);
        } catch (ParseException e) {
            e.printStackTrace();
        }
        Timestamp timestamp = new Timestamp(date.getTime());

        approFileInfo.setFileTime(timestamp);

        Timestamp now = new Timestamp(new Date().getTime());
        approFileInfo.setCreateTime(now);
        approFileInfo.setUpdateTime(now);
        return  approFileInfo;
    }

    private List<String> queryFileName(Config config){

        List<String> fileList = new ArrayList<>();

        //1.查看文件夹中有无文件
        File sourDir = new File(config.getSourDir());
        String[] fileStrings = sourDir.list();

        if (fileStrings==null){
            throw new AAOSException("[ AAOS ] "+config.getBank()+"(DBModule) path: "+sourDir.getPath()+" is a file, please check");
        }

        if (fileStrings.length == 0){
            //文件夹为空，没有文件
            return fileList;
        }

        //2.筛选名称满足过滤条件的ok文件，并将ok文件转换为txt文件，放入list
        for (String okFileName:fileStrings){
            //过滤所有ok文件
            if (okFileName.matches(config.getFileNameFilter())){
                String fileName = okFileName.substring(0,okFileName.lastIndexOf("."))+".txt";
                //.ok文件对应的源文件存在才放入list
                File sourFile = new File(config.getSourDir()+"/"+fileName);
                if (sourFile.exists()){
                    fileList.add(fileName);
                }
            }
        }

        return fileList;
    }

    /*
     * 将文件移至错误文件夹并生成对应.ok文件
     * approFileInfo 文件     config 配置
     */
    private void backUpToErr(ApproFileInfo approFileInfo,Config config){

        File file = new File(approFileInfo.getFilePath());
        if (!file.exists()){
            log.info("[ AAOS ] {} (DBModule) {} not exit,back up over",config.getBank(),approFileInfo.getFileName());
            return;
        }
        //备份
        //TODO
        try {
            FileUtils.copyFile(new File(config.getSourDir()+"/"+approFileInfo.getFileName()),new File(config.getErrorDir()+"/"+approFileInfo.getFileName()));
        } catch (IOException e) {
            throw new AAOSException("file copy error",e);
        }
    }


    /*
     * 删除源文件及.ok文件
     * approFileInfo 文件     config 配置
     */
    private void deleteSour(ApproFileInfo approFileInfo,Config config,String stage){

        String path = approFileInfo.getFilePath();
        String okPath = path.substring(0,path.lastIndexOf(".")) + ".ok";

        new File(path).delete();
        new File(okPath).delete();
        log.info("[ AAOS ] {} (DBModule){}{} source delete over",config.getBank(),stage,approFileInfo.getFileName());

    }


    /*
     * 将文件对应的记录插入行表
     * approFileInfo 文件      config 配置
     * 返回值int 为插入数据库的行数，即文件中的记录数
     * */
    private int lineInDb(ApproFileInfo approFileInfo,Config config,String stage)  {

        File file = new File(approFileInfo.getFilePath());
        if (!file.exists()){
            log.info("[ AAOS ] {} (DBModule){}{} not exist,line insert over",config.getBank(),stage,approFileInfo.getFileName());
            return -1;
        }

        String line;
        int count = 0;

        try {
            FileInputStream inputStream = new FileInputStream(approFileInfo.getFilePath());
            InputStreamReader isr = new InputStreamReader(inputStream,config.getSourCode());
            BufferedReader reader = new BufferedReader(isr);

            while (!FixedLenUtil.isBlank(line = reader.readLine())){
                count++;
                //将json字符串转换为java bean
                ApproLineInfo approLineInfo = JSON.parseObject(line,new TypeReference<ApproLineInfo>(){});
                approLineInfo.setFile_id((int) approFileInfo.getId());
                try{
                    approLineMapper.insert(approLineInfo);
                } catch (Exception e){
                    log.error("[ AAOS ] {} (DBModule) {} row {} line insert error",config.getBank(),approFileInfo.getFileName(),count);
                    throw new AAOSException("AAOS_LINE_SAVE_ERROR", e);
                }
            }
            inputStream.close();
            isr.close();
            reader.close();
        } catch (Exception e) {
            log.error("[ AAOS ] {} (DBModule) {} line in db error ",config.getBank(),approFileInfo.getFileName(),e);
            throw new AAOSException("[ AAOS ] "+config.getBank()+" (DBModule) "+approFileInfo.getFileName()+" line in db error ",e);
        }

        return count;
    }

    /*
     * 将文件移至备份文件夹并生成对应.ok文件
     * approFileInfo 文件     config 配置
     */
    private void backUp(ApproFileInfo approFileInfo,Config config,String stage){

        File file = new File(approFileInfo.getFilePath());
        if (!file.exists()){
            log.info("[ AAOS ] {} (DBModule){}{} not exit,back up over",config.getBank(),stage,approFileInfo.getFileName());
            return;
        }
        //备份
        try {
            FileUtils.copyFile(new File(config.getSourDir()+"/"+approFileInfo.getFileName()),new File(config.getBackDir()+"/"+approFileInfo.getFileName()));
        } catch (IOException e) {
            throw new AAOSException("file cpoy error",e);
        }
        log.info("[ AAOS ] {} (DBModule){}{} back up over",config.getBank(),stage,approFileInfo.getFileName());
    }


}
